package com.jmp.api.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.jmp.api.dto.TemplateCreateResultDto;
import com.jmp.api.dto.TemplateParamDto;
import com.jmp.api.service.SunCodeService;
import com.jmp.api.service.TemplateCardFacadeService;
import com.jmp.autocofiguration.util.OssUtil2;
import com.jmp.base.TemplateApi;
import com.jmp.base.dto.GoodsGroupBuyingDto;
import com.jmp.base.dto.TemplateListDto;
import com.jmp.base.dto.TemplateListVo;
import com.jmp.base.enums.TemplateConfigTypeEnum;
import lombok.extern.slf4j.Slf4j;
import one.stand.advice.ResultEnum;
import one.stand.dto.ConvertUtils;
import one.stand.dto.LiveRoomDto;
import one.stand.dto.PageResponse2;
import one.stand.mapper.*;
import one.stand.model.*;
import one.stand.service.*;
import one.stand.service.sys.UserContentService;
import one.stand.util.AssertUtil;
import one.stand.util.ImageGenerateUtil2;
import one.stand.util.OneListUtil;
import one.stand.util.SqlUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TemplateCardFacadeServiceImpl implements TemplateCardFacadeService {

    @Autowired
    private BusinessCardBaseService businessCardBaseService;
    @Autowired
    private CompanyBaseService companyBaseService;
    @Autowired
    private GoodsBaseService goodsBaseService;
    @Autowired
    private GoodsPhotoService goodsPhotoService;
    @Autowired
    private RecordPublicService recordPublicService;

    @Autowired
    private TemplateConfigMapperExt templateConfigMapperExt;
    @Autowired
    private TemplateCardMapperExt templateCardMapperExt;
    @Autowired
    private TemplateMapperExt templateMapperExt;
    @Autowired
    private WebsiteMapperExt websiteMapperExt;
    @Autowired
    private SunCodeService sunCodeService;
    @Autowired
    private ArticleMapperExt articleMapperExt;

    @Override
    public void removeByCompanyId(Integer companyId) {
        List<BusinessCard> cardList = businessCardBaseService.getByCompanyId(companyId);
        for (BusinessCard card : cardList) {
            removeByCard(card.getCardId());
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public void removeByCard(Integer cardId) {
        String cardIdStr = String.valueOf(cardId);
        String cardLike = SqlUtil.likeRight(cardId + ";");
        for (TemplateConfigTypeEnum configTypeEnum : TemplateConfigTypeEnum.values()) {
            if (configTypeEnum.isUpdateCard()) {
                templateCardMapperExt.deleteByCardAndConfig(cardIdStr, configTypeEnum.getType());
                templateCardMapperExt.deleteByCardLikeConfig(cardLike, configTypeEnum.getType());
            }
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public void removeByArticle(Integer articleId) {
        log.info("removeByArticle:{}", articleId);
        String rateIdsLike = SqlUtil.likeLeft(";;" + articleId);
        for (TemplateConfigTypeEnum configTypeEnum : TemplateConfigTypeEnum.values()) {
            if (configTypeEnum.isUpdateArticle()) {
                templateCardMapperExt.deleteByCardLikeConfig(rateIdsLike, configTypeEnum.getType());
            }
        }
    }

    @Override
    @Transactional
    public void remove(Integer templateId) {
        templateCardMapperExt.deleteByTemplateId(templateId, null, null, null);
    }

    @Override
    public String preview(Template template, List<TemplateConfig> configs, TemplateParamDto templateParamDto) {
        TemplateCreateResultDto resultDto = createCard(template, configs, templateParamDto);
        return resultDto.getCardUrl();
    }

    @Override
    @Transactional
    public String create(Integer templateId, TemplateParamDto templateParamDto) {
        TemplateCreateResultDto resultDto = createCard(templateId, templateParamDto);

        if (Objects.nonNull(templateParamDto.getDiyImg()) || StringUtils.isNoneBlank(templateParamDto.getDiyUrl())) {// diy
            templateCardMapperExt.deleteByTemplateId(templateId, templateParamDto.getRateId(), resultDto.getConfigStr(), TemplateApi.CARD_TYPE_DIY);

            TemplateCard templateCard = new TemplateCard();
            templateCard.setTemplateId(templateId);
            templateCard.setRateIds(templateParamDto.getRateId());
            templateCard.setType(TemplateApi.CARD_TYPE_DIY);
            templateCard.setImgUrl(resultDto.getCardUrl());
            templateCard.setConfigStr(resultDto.getConfigStr());
            templateCard.setParamStr(resultDto.paramStrHash());
            templateCardMapperExt.insertSelective(templateCard);

            templateMapperExt.updateDiyNum(templateId);
        } else {// default
            templateCardMapperExt.deleteByTemplateId(templateId, templateParamDto.getRateId(), resultDto.getConfigStr(), TemplateApi.CARD_TYPE_DEFAULT);

            TemplateCard templateCard = new TemplateCard();
            templateCard.setTemplateId(templateId);
            templateCard.setRateIds(templateParamDto.getRateId());
            templateCard.setType(TemplateApi.CARD_TYPE_DEFAULT);
            templateCard.setImgUrl(resultDto.getCardUrl());
            templateCard.setConfigStr(resultDto.getConfigStr());
            templateCard.setParamStr(resultDto.paramStrHash());
            templateCardMapperExt.insertSelective(templateCard);
        }

        return resultDto.getCardUrl();
    }

    @Override
    public String get(Integer templateId, TemplateParamDto templateParamDto) {
        TemplateCard templateCard = templateCardMapperExt.selectByTempalte(templateId, templateParamDto.getRateId(), TemplateApi.CARD_TYPE_DIY);
        if (Objects.isNull(templateCard)) {
            templateCard = templateCardMapperExt.selectByTempalte(templateId, templateParamDto.getRateId(), TemplateApi.CARD_TYPE_DEFAULT);
        }
        if (Objects.isNull(templateCard)) {
            return create(templateId, templateParamDto);
        }
        return templateCard.getImgUrl();
    }

    @Override
    public TemplateListVo getList(TemplateListDto listDto) {
        PageHelper.startPage(listDto.getPageNum(), listDto.getPageSize());
        List<Template> templates = templateMapperExt.selectList(UserContentService.TYPE_COMPANY, listDto.getCompanyId(), UserContentService.TYPE_CARD);
        PageResponse2<TemplateListVo.Card> cardPage = ConvertUtils.page2(templates, template -> {
            String imgUrl = get(template.getId(), TemplateParamDto.builder().cardId(listDto.getCardId()).build());
            return TemplateListVo.Card.builder()
                    .id(template.getId())
                    .cardUrl(imgUrl)
                    .imgUrlWidth(template.getImgUrlWidth())
                    .imgUrlHeight(template.getImgUrlHeigth())
                    .imgWidth(template.getImgWidth())
                    .imgHeight(template.getImgHeight())
                    .positionWidth(template.getPositionWidth())
                    .positionHeight(template.getPositionHeight())
                    .build();
        });

        TemplateListVo vo = TemplateListVo.builder()
                .cardPage(cardPage)
                .build();
        return vo;
    }

    @Override
    @Async("asyncServiceExecutor")
    public void getListAsync(TemplateListDto listDto) {
        getList(listDto);
    }

    private TemplateCreateResultDto createCard(Integer templateId, TemplateParamDto templateParamDto) {

        Template template = templateMapperExt.selectByPrimaryKey(templateId);
        List<TemplateConfig> configs = templateConfigMapperExt.selectByTemplateId(templateId);

        return createCard(template, configs, templateParamDto);
    }

    private TemplateCreateResultDto createCard(Template template, List<TemplateConfig> configs, TemplateParamDto templateParamDto) {
        List<TemplateConfig> configList = getConfigs(configs, templateParamDto.getConfigExclude());
        List<ImageGenerateUtil2.Entity> entities = getConfigEntity(configList, templateParamDto);
        String configStr = getConfigStr(configList);

        ImageGenerateUtil2.TemplateEntity templateEntity = new ImageGenerateUtil2.TemplateEntity();
        String backUrl;
        if (StringUtils.isNoneBlank(templateParamDto.getBackgroundUrl())) {
            backUrl = templateParamDto.getBackgroundUrl();
        } else {
            backUrl = template.getImgUrl();
        }
        templateEntity.setBackUrl(backUrl);
        templateEntity.setEntities(entities);
        InputStream inputStream = ImageGenerateUtil2.cardDiy(templateEntity);

        String cardUrl = OssUtil2.upload(inputStream, OssUtil2.FileSuffix.png);
        return TemplateCreateResultDto.builder()
                .cardUrl(cardUrl)
                .configStr(configStr)
                .paramStr(JSON.toJSONString(templateEntity))
                .build();
    }

    private List<ImageGenerateUtil2.Entity> getConfigEntity(List<TemplateConfig> configs, TemplateParamDto templateParamDto) {
        List<ImageGenerateUtil2.Entity> entities = Lists.newArrayList();

        BusinessCard businessCardModel = null;
        Company companyModel = null;
        if (Objects.nonNull(templateParamDto.getCardId())) {
            businessCardModel = businessCardBaseService.getCustomer(templateParamDto.getCardId());
            companyModel = companyBaseService.getById(businessCardModel.getCompanyId());
        }
        Goods goods = null;
        if (Objects.nonNull(templateParamDto.getGoodsId())) {
            goods = goodsBaseService.get(templateParamDto.getGoodsId());
        }
        ArticleWithBLOBs article = null;
        if (Objects.nonNull(templateParamDto.getArticleId())) {
            article = articleMapperExt.selectByPrimaryKey(templateParamDto.getArticleId());
        }
        LiveRoomDto liveRoomDto = null;
        if (Objects.nonNull(templateParamDto.getRoomId())) {
            liveRoomDto = recordPublicService.getLiveRoom(templateParamDto.getRoomId());
        }
        GoodsGroupBuyingDto goodsGroupBuyingDto = goodsBaseService.getGroup(templateParamDto.getGroupId());

        TemplateConfigTypeEnum typeEnum;
        String url;
        InputStream imgStream;
        String content;
        for (TemplateConfig config : configs) {

            url = null;
            imgStream = null;
            content = config.getFontContent();
            typeEnum = TemplateConfigTypeEnum.getByType(config.getType());

            switch (typeEnum) {
                case IMG_DIY:
                    if (Objects.nonNull(templateParamDto.getDiyImg())) {
                        imgStream = templateParamDto.getDiyImg();
                    } else if (Objects.nonNull(templateParamDto.getDiyUrl())) {
                        url = templateParamDto.getDiyUrl();
                    }
                    break;
                case CARD_SUNCODE:
                    url = sunCodeService.getWxTwoCode(businessCardModel.getCardId(), false);
                    break;
                case CARD_IMG:
                    url = businessCardModel.getLogo();
                    break;
                case CARD_NAME:
                    content = businessCardModel.getName();
                    break;
                case CARD_TEL:
                    content = businessCardModel.getPhone();
                    break;
                case CARD_POSITION:
                    content = businessCardModel.getPosition();
                    break;
                case CARD_EMAIL:
                    content = businessCardModel.getEmail();
                    break;
                case CARD_WX_CODE:
                    url = businessCardModel.getWxCode();
                    break;
                case COMPANY_NAME:
                    content = companyModel.getCompanyName();
                    break;
                case COMPANY_IMG:
                    url = companyModel.getCompanyLogo();
                    break;
                case COMPANY_ADDRESS:
                    Website websiteModel = websiteMapperExt.selectByCompanyId(businessCardModel.getCompanyId());
                    if (Objects.nonNull(websiteModel)) {
                        content = websiteModel.getAddress();
                    }
                    break;
                case GOODS_IMG:
                    url = goodsPhotoService.getPath(templateParamDto.getGoodsId());
                    break;
                case GOODS_PRICE:
                    if (Objects.nonNull(goods)) {
                        content = String.valueOf(goods.getPrice());
                    }
                    break;
                case GOODS_NAME:
                    if (Objects.nonNull(goods)) {
                        content = goods.getGoodsName();
                    }
                    break;
                case GOODS_SUNCODE:
                    List<GoodSpec> specs = goodsBaseService.getSpecs(templateParamDto.getGoodsId());
                    AssertUtil.nonNull(specs, ResultEnum.PARAM_CHECK, "商品属性为空，无法分享");
                    url = sunCodeService.goodsSuncode(specs.get(0).getSpecId(), templateParamDto.getCardId());
                    break;
                case ARTICLE_TITLE:
                    content = article.getTitle();
                    break;
                case ARTICLE_SHARE_URL:
                    url = article.getShareCard();
                    break;
                case ARTICLE_H5_URL:
                    imgStream = getTwoUrl(templateParamDto.getArticleH5Url());
                    break;
                case ROOM_NAME:
                    content = liveRoomDto.getName();
                    break;
                case ROOM_PHOTO:
                    url = liveRoomDto.getPhoto();
                    break;
                case ROOM_TYPE:
                    content = liveRoomDto.getTypeName();
                    break;
                case ROOM_SUNCODE:
                    url = sunCodeService.room(liveRoomDto.getRoomId(), liveRoomDto.getCompanyId());
                    break;
                case IMG_POST:
                    url = config.getImgUrl();
                    break;
                case GROUP_IMG:
                    url = goodsGroupBuyingDto.getPhoto();
                    break;
                case GROUP_TITLE:
                    content = goodsGroupBuyingDto.getTitle();
                    break;
                case GROUP_PRICE:
                    content = templateParamDto.getGroupPrice();
                    break;
                case GROUP_H5_URL:
                    imgStream = getTwoUrl(templateParamDto.getGroupH5Url());
                    break;
                default:
                    log.error("type({}) is missing", config.getType());
            }
            if (typeEnum.isImg()) {
                if (StringUtils.isBlank(url) && Objects.isNull(imgStream)) {
                    log.warn("template img url is null");
                    continue;
                }
                ImageGenerateUtil2.ImageEntity card = ImageGenerateUtil2.ImageEntity.builder()
                        .xAxis(config.getPointX())
                        .yAxis(config.getPointY())
                        .url(url)
                        .inputStream(imgStream)
                        .width(config.getImgWidth())
                        .height(config.getImgHeight())
                        .wcenter(Objects.nonNull(config.getFontCenter()))
                        .build();
                entities.add(card);
            } else if (typeEnum.isFont()) {
                if (StringUtils.isBlank(content)) {
                    log.warn("template font content is null");
                    continue;
                }
                Color color = null;
                if (StringUtils.isNoneBlank(config.getFontColor())) {
                    String[] rgb = config.getFontColor().split(",");
                    color = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
                }

                ImageGenerateUtil2.FontEntity name = ImageGenerateUtil2.FontEntity.builder()
                        .xAxis(config.getPointX())
                        .yAxis(config.getPointY())
                        .wcenter(Objects.nonNull(config.getFontCenter()))
                        .content(content)
                        .size(config.getFontSize())
                        .style(config.getFontStyle())
                        .color(color)
                        .build();
                entities.add(name);
            }
        }

        return entities;
    }

    private List<TemplateConfig> getConfigs(List<TemplateConfig> configs, List<TemplateConfigTypeEnum> configExclude) {

        if (OneListUtil.isEmpty(configExclude)) {
            return configs;
        }

        TemplateConfigTypeEnum typeEnum;
        List<TemplateConfig> configList = Lists.newArrayList();
        for (TemplateConfig config : configs) {
            typeEnum = TemplateConfigTypeEnum.getByType(config.getType());
            if (configExclude.contains(typeEnum)) {//排除这些配置
                continue;
            }

            configList.add(config);
        }
        return configList;
    }

    private String getConfigStr(List<TemplateConfig> configs) {
        return configs.stream().map(templateConfig -> String.valueOf(templateConfig.getType())).sorted().collect(Collectors.joining(","));
    }

    private InputStream getTwoUrl(String content) {
        if (StringUtils.isBlank(content)) {
            return null;
        }

        Hashtable hints = new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, 220, 220, hints);
            int width = bitMatrix.getWidth();
            int height = bitMatrix.getHeight();
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
                }
            }
            return bufferedImageToInputStream(image);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 将BufferedImage转换为InputStream
     */
    public InputStream bufferedImageToInputStream(BufferedImage image) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        return new ByteArrayInputStream(os.toByteArray());
    }
}
